package ax

import (
	"encoding/json"
	"log"
	"sync"
)

type jsonClientMessage struct {
	MsgType string `json:"type"`
	Data interface{} `json:"data"`
}

type JsonMessageHandler func (c *Client, 
			  msgtype string, data interface{})
type RawMessageHandler func (c *Client, data []byte) bool

var (
	msgMap = make(map[string]JsonMessageHandler)
	msgMutex sync.RWMutex
	rawHandler RawMessageHandler
)

func JsonSend(c *Client, msgtype string, arg interface{}) error {
	msg := &jsonClientMessage {
		MsgType: msgtype,
		Data: arg,
	}
	data, err := json.Marshal(msg)
	if err != nil {
		log.Printf("messaging.Send error %+v", err)
		return err
	}
	c.Send(data)
	return nil
}

func JsonInvalidMessage(c *Client, m interface{}) {
	log.Printf("Recv invalid JSON message (%+v, %+v)\n", c, m)
}

func JsonHandleMessage(c *Client, msgtype string, data interface{}) bool {
	msgMutex.RLock()
	handler, exists := msgMap[msgtype]
	if !exists {
		msgMutex.RUnlock()
		return false
	}
	handler(c, msgtype, data)
	msgMutex.RUnlock()
	return true
}

func JsonParse(c *Client, m interface{}) {
	var msgtype string
	var data interface{}
	var ok bool

	switch v := m.(type) {
	case map[string]interface{}:
		msgtype, ok = v["type"].(string)
		if !ok {
			JsonInvalidMessage(c, m)
		}
		data, ok = v["data"]
		if !ok {
			JsonInvalidMessage(c, m)
		}
		JsonHandleMessage(c, msgtype, data)
	default:
		JsonInvalidMessage(c, m)
	}
}

func jsonRecv(c *Client, data []byte) bool {
	var m interface{}
	err := json.Unmarshal(data, &m)
	if err != nil {
		return false
	}
	JsonParse(c, m)
	return true
}

func rawRecv(c *Client, data []byte) bool {
	r := false
	msgMutex.RLock()
	if rawHandler != nil {
		r = rawHandler(c, data)
	}
	msgMutex.RUnlock()
	return r
}

// Entrypoint for incoming messages
func onRecv(c *Client, data []byte) {
	r := rawRecv(c, data)
	if !r {
		jsonRecv(c, data)
	}
}

func OnJson(msgtype string, handler JsonMessageHandler) {
	msgMutex.Lock()
	msgMap[msgtype] = handler
	msgMutex.Unlock()
}

func OnRaw(handler RawMessageHandler) {
	msgMutex.Lock()
	rawHandler = handler
	msgMutex.Unlock()
}
